package com.everwin.web.service.impl;

import cn.hutool.core.util.IdUtil;
import com.everwin.web.minio.MinioProperties;
import com.everwin.web.minio.MinioUtil;
import com.everwin.web.service.MinioService;
import io.minio.messages.Bucket;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;

@Service
@Slf4j
public class MinioServiceImpl implements MinioService {

    @Autowired
    private MinioUtil minioUtil;
    @Autowired
    private MinioProperties minioProperties;

    @Override
    public boolean bucketExists(String bucketName) {
        return minioUtil.bucketExists(bucketName);
    }


    @Override
    public void makeBucket(String bucketName) {
        minioUtil.makeBucket(bucketName);
    }

    @Override
    public List<String> listBucketName() {
        return minioUtil.listBucketNames();
    }

    @Override
    public List<Bucket> listBuckets() {
        return minioUtil.listBuckets();
    }

    @Override
    public boolean removeBucket(String bucketName) {
        return minioUtil.removeBucket(bucketName);
    }


    @Override
    public List<String> listObjectNames(String bucketName) {
        return minioUtil.listObjectNames(bucketName);
    }


    /**
     * Minio实现存储文件自动去重的方式是使用哈希值对文件进行唯一标识。
     * 具体实现方式是在上传文件时，计算文件的哈希值并与已有的文件进行比较。
     * 如果已经有相同哈希值的文件，就不再将该文件存储到Minio中，而是在文件系统中创建一个硬链接，指向已经存在的文件。
     * 这种方式可以避免重复存储相同的文件，节省存储空间，提高存储效率。
     * 同时，由于哈希值的唯一性，确保了文件的完整性和一致性。
     * Minio使用的哈希算法是SHA-256，它可以快速且可靠地计算文件的哈希值，并且具有较低的冲突率。
     * 通过这种方式，Minio可以实现高效的文件去重，从而优化存储空间的利用
     *
     * @param file
     * @param bucketName
     * @param fileType
     * @return
     */
    @Override
    public String putObject(MultipartFile file, String bucketName, String fileType) {
        try {
            bucketName = StringUtils.isNotBlank(bucketName) ? bucketName : minioProperties.getBucketName();
            if (!this.bucketExists(bucketName)) {
                this.makeBucket(bucketName);
            }
            String fileName = file.getOriginalFilename();

            assert fileName != null;
            String objectName = IdUtil.fastSimpleUUID()
                    + fileName.substring(fileName.lastIndexOf("."));

            // 这里 同一bucket中使用 原fileName ,文件名相同的话 会导致 之前上传的文件会被覆盖
            // 但是如果 同一文件更改了名字进行上传的话 同一文件(不同名称)会多次上传 --> 这种应该算正常
            // 同一文件(文件名也相同) 此时不会重复上传

            minioUtil.putObject(bucketName, file, objectName,fileType);

          /*
            Read Only - download
            Write Only - upload
            Read and Write - public
            设置 任一访问策略后 可以通过此种方式访问 url 永久有效
            */
            return minioProperties.getEndpoint() + ":" + minioProperties.getPort() + "/" + bucketName + "/" + objectName;

            // 应该使用此种方式获取url ,但是有效期只有7天 后面附带加密等其他参数
           // return minioUtil.getObjectUrl(bucketName, objectName);
        } catch (Exception e) {
            log.error("上传失败:{}",e.getMessage());
            return "上传失败";
        }
    }

    @Override
    public InputStream downloadObject(String bucketName, String objectName) {
        return minioUtil.getObject(bucketName,objectName);
    }


    // 获取文件输出流下载文件
    @SneakyThrows
    @Override
    public OutputStream downloadFile(String bucketName, String objectName) {
        InputStream inputStream = minioUtil.getObject(bucketName, objectName);

        OutputStream outputStream = new FileOutputStream(objectName);
        FileCopyUtils.copy(inputStream,outputStream);

        return outputStream;
    }

    @Override
    public boolean removeObject(String bucketName, String objectName) {
        return minioUtil.removeObject(bucketName, objectName);
    }

    @Override
    public boolean removeListObject(String bucketName, List<String> objectNameList) {
        return minioUtil.removeObject(bucketName,objectNameList);
    }

    @Override
    public String getObjectUrl(String bucketName,String objectName) {
        return minioUtil.getObjectUrl(bucketName, objectName);
    }
}
